/*
 * Daniel R Padilla
 *
 * Copyright (c) 2008, Daniel R Padilla
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.databasefacade.util;


import java.io.File;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.databasefacade.exception.DatabaseFacadeException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/**
 * The class provides methods that hopefully
 * make it easier to process an XML document.
 * 
 *  @Author Daniel 
 */
public class XMLParseUtil
{
	
	private DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
	public Document getDocument(File file)
		throws DatabaseFacadeException
	{
		Document doc = null;
		try {
			DocumentBuilder builder = docFactory.newDocumentBuilder();
			doc = builder.parse(file);
		} catch ( Exception e ) {
			throw new DatabaseFacadeException(e,
				"Daniel R Padilla was unable to open:" + file.getAbsolutePath());
		}
		return doc;
	}
    
	/**
	 * The method gets the data for the tag that is lowest in
	 * the hierarchy. So level 1 is the root tag that is searched
	 * until the final tag is reached and then we return the data
	 * in the last tag.
	 * 
	 * Only attributes and values of the final tag are returned in
	 * the XMLDataRecordList. If children exists for the final tag
	 * then a request for that children must be made separately.
	 * 
	 * @param tagHierachy
	 * @return
	 */
	public void assignTagXMLData(XMLDataRecordList data, ArrayList tagHierarchy, Document doc)
	{
		NodeList root = doc.getElementsByTagName((String) tagHierarchy.get(0));
		assignTagXMLData(data, tagHierarchy, root);
	}

	public void assignTagXMLData(XMLDataRecordList data, ArrayList tagHierarchy, NodeList root)
	{
		int len = root.getLength();
		for ( int i = 0; i < len; i++ ) {
			Node n = root.item(i);
			int level = 1;
			processHierarchy(level, data, tagHierarchy, n);
		}
	}

	public void processHierarchy(int level, XMLDataRecordList data, ArrayList tagHierarchy, Node node)
	{
		if ( node.getNodeType() == Node.ELEMENT_NODE ) {
			int hLen = tagHierarchy.size();
			int nextLevel = level + 1;
			if ( hLen >= nextLevel ) {
				String tag = (String) tagHierarchy.get(level);
				Element e = (Element) node;
				NodeList nodes = e.getElementsByTagName(tag); 
				int len = nodes.getLength();
				for ( int i = 0; i < len; i++ ) {
					Node nextNode = nodes.item(i);
					processHierarchy(nextLevel, data, tagHierarchy, nextNode);
				}
			} else {
				processNode(node, data);	
			}
		}
	}
  
	private void processNode(Node n, XMLDataRecordList data)
	{
		XMLDataRecord rec = new XMLDataRecord();
		String name = n.getNodeName();
		rec.setName(name);
		processAttributes(n, rec);
		assignValue(n, rec);
		NodeList nodes = n.getChildNodes();
		rec.setChildNodes(nodes); 
		data.add(rec);
	}

	private void assignValue(Node node, XMLDataRecord rec)
	{
		if ( node.hasChildNodes() ) {
			NodeList valuesList = node.getChildNodes();
			for ( int v = 0; v < valuesList.getLength(); v++ ) {
				Node value = valuesList.item(v);
				if ( value.getNodeValue() != null ) {
					rec.setValue(value.getNodeValue().trim());
				}
			}
		}
	}
    
	private void processAttributes(Node node, XMLDataRecord rec)
	{
		if ( node.hasAttributes() ) {
			NamedNodeMap attribMap = node.getAttributes();
			for ( int v = 0; v < attribMap.getLength(); v++ ) {
				Node attrib = attribMap.item(v);
				if ( attrib.getNodeValue() != null ) {
					String attribName = attrib.getNodeName();
					;
					String attribValue = attrib.getNodeValue().trim();
					rec.setAttribute(attribName, attribValue);                    
				}
			}
		}
	}
    
	public static void main(String args[])
		throws Exception
	{
		XMLParseUtil xmlutil = new XMLParseUtil();
		String filePath = args[0];
		File file = new File(filePath);
		Document doc = xmlutil.getDocument(file);
		XMLDataRecordList data = new XMLDataRecordList();
		ArrayList tagHierarchy = new ArrayList();
    	
		// Test first level
		tagHierarchy.add("DatabaseFacadeConnection");
		xmlutil.assignTagXMLData(data, tagHierarchy, doc);
		System.out.println(data.toString());
    	
		// Test second level
		tagHierarchy.add("DatabaseVendor");
		data = new XMLDataRecordList();
		xmlutil.assignTagXMLData(data, tagHierarchy, doc);
		System.out.println(data.toString());
    	
		// Test third level with a list of methods
		tagHierarchy = new ArrayList();
		tagHierarchy.add("DatabaseFacadeConnection");
		tagHierarchy.add("SetupMethods");
		tagHierarchy.add("Method");
		data = new XMLDataRecordList();
		xmlutil.assignTagXMLData(data, tagHierarchy, doc);
		System.out.println(data.toString());
	}
}
